
package com.doudou.pay.helper.commons;

import java.io.*;
import java.nio.charset.Charset;

public class StreamUtils {
  public static final int BUFFER_SIZE = 1024 * 32;

  /**
   * Copy the contents of the given InputStream into a new byte array. Leaves
   * the stream open when done.
   * 
   * @param in the stream to copy from
   * @return the new byte array that has been copied to
   * @throws IOException in case of I/O errors
   */
  public static byte[] copyToByteArray(InputStream in) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream(BUFFER_SIZE);
    copy(in, out);
    return out.toByteArray();
  }

  /**
   * Copy the contents of the given InputStream into a String. Leaves the stream
   * open when done.
   *
   * @param in the InputStream to copy from
   * @return the String that has been copied to
   * @throws IOException in case of I/O errors
   */
  public static String copyToString(InputStream in, Charset charset) throws IOException {
    StringBuilder out = new StringBuilder();
    InputStreamReader reader = new InputStreamReader(in, charset);
    char[] buffer = new char[BUFFER_SIZE];
    int bytesRead = -1;
    while ((bytesRead = reader.read(buffer)) != -1) {
      out.append(buffer, 0, bytesRead);
    }
    return out.toString();
  }

  /**
   * Copy the contents of the given byte array to the given OutputStream. Leaves
   * the stream open when done.
   *
   * @param in the byte array to copy from
   * @param out the OutputStream to copy to
   * @throws IOException in case of I/O errors
   */
  public static void copy(byte[] in, OutputStream out) throws IOException {
    out.write(in);
  }

  /**
   * Copy the contents of the given String to the given output OutputStream.
   * Leaves the stream open when done.
   *
   * @param in the String to copy from
   * @param charset the Charset
   * @param out the OutputStream to copy to
   * @throws IOException in case of I/O errors
   */
  public static void copy(String in, Charset charset, OutputStream out) throws IOException {
    Writer writer = new OutputStreamWriter(out, charset);
    writer.write(in);
    writer.flush();
  }

  /**
   * Copy the contents of the given InputStream to the given OutputStream.
   * Leaves both streams open when done.
   *
   * @param in the InputStream to copy from
   * @param out the OutputStream to copy to
   * @return the number of bytes copied
   * @throws IOException in case of I/O errors
   */
  public static int copy(InputStream in, OutputStream out) throws IOException {
    int byteCount = 0;
    byte[] buffer = new byte[BUFFER_SIZE];
    int bytesRead = -1;
    while ((bytesRead = in.read(buffer)) != -1) {
      out.write(buffer, 0, bytesRead);
      byteCount += bytesRead;
    }
    out.flush();
    return byteCount;
  }

  /**
   * Returns a variant of the given {@link InputStream} where calling
   * {@link InputStream#close() close()} has no effect.
   *
   * @param in the InputStream to decorate
   * @return a version of the InputStream that ignores calls to close
   */
  public static InputStream nonClosing(InputStream in) {
    return new NonClosingInputStream(in);
  }

  /**
   * Returns a variant of the given {@link OutputStream} where calling
   * {@link OutputStream#close() close()} has no effect.
   * 
   * @param out the OutputStream to decorate
   * @return a version of the OutputStream that ignores calls to close
   */
  public static OutputStream nonClosing(OutputStream out) {
    return new NonClosingOutputStream(out);
  }

  private static class NonClosingInputStream extends FilterInputStream {

    public NonClosingInputStream(InputStream in) {
      super(in);
    }

    @Override
    public void close() throws IOException {
    }
  }

  private static class NonClosingOutputStream extends FilterOutputStream {

    public NonClosingOutputStream(OutputStream out) {
      super(out);
    }

    @Override
    public void write(byte[] b, int off, int let) throws IOException {
      // It is critical that we override this method for performance
      out.write(b, off, let);
    }

    @Override
    public void close() throws IOException {
    }
  }
}
